#pragma once

#include "../communal/Communication.hpp"
#include "../communal/Message.hpp"
#include <set>

namespace xu
{
    namespace server
    {
        class ProviderManager
        {
        public:
            using Ptr = std::shared_ptr<ProviderManager>;
            struct Provider
            {
                using Ptr = std::shared_ptr<Provider>;
                std::mutex _mutex;
                BaseConnection::Ptr _conn;         // 连接
                Address _host;                     // 主机
                std::vector<std::string> _methods; // 服务名

                Provider(const BaseConnection::Ptr &conn, const Address &host)
                    : _conn(conn),
                      _host(host)
                {
                }

                void AddMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    _methods.push_back(method);
                }
            };

            // 添加一个服务提供者
            void addProvider(const BaseConnection::Ptr &c, const Address &h, const std::string &method)
            {
                LOG(LogLevel::DEBUG)<<"提供者上线了";
                Provider::Ptr provider;
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = conns.find(c);
                    if (it == conns.end())
                    {
                        provider = std::make_shared<Provider>(c, h);
                        conns.insert(std::make_pair(c, provider));
                    }
                    else
                    {
                        provider = it->second;
                    }

                    auto &provider_set = providers[method];
                    provider_set.insert(provider);
                }

                provider->AddMethod(method);
            }

            // 当一个服务提供者断开连接的时候，获取他的信息--用于进行服务下线通知
            Provider::Ptr getProvider(const BaseConnection::Ptr &c)
            {
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = conns.find(c);
                    if (it != conns.end())
                    {
                        return it->second;
                    }
                }

                return Provider::Ptr();
            }

            // 当一个服务提供者断开连接的时候，删除它的关联信息
            void delProvider(const BaseConnection::Ptr &c)
            {
                LOG(LogLevel::DEBUG)<<"提供者下线了";
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = conns.find(c);
                    if (it == conns.end())
                    {
                        LOG(LogLevel::ERROR) << "服务提供者不存在";
                        return;
                    }

                    // 这里可能出错
                    for (auto &e : it->second->_methods)
                    {
                        providers[e].erase(it->second);
                    }

                    conns.erase(c);
                }
                
            }

            std::vector<Address> methodHosts(const std::string &method)
            {
                LOG(LogLevel::DEBUG)<<"将数据推给发现者";
                std::vector<Address> ret;
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = providers.find(method);
                    if (it == providers.end())
                    {
                        return std::vector<Address>();
                    }

                    for (auto &e : it->second)
                    {
                        ret.push_back(e->_host);
                    }
                }

                return ret;
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Provider::Ptr>> providers;
            std::unordered_map<BaseConnection::Ptr, Provider::Ptr> conns;
        };

        class DiscovererManager
        {
        public:
            using Ptr = std::shared_ptr<DiscovererManager>;
            struct Discoverer
            {
                using Ptr = std::shared_ptr<Discoverer>;
                std::mutex _mutex;
                BaseConnection::Ptr _conn;
                std::vector<std::string> _methods;

                Discoverer(const BaseConnection::Ptr &c) : _conn(c)
                {}

                void AddMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    _methods.push_back(method);
                }
            };

            void AddDiscoverer(const BaseConnection::Ptr &c, const std::string &method)
            {
                LOG(LogLevel::DEBUG)<<"发现者上线了";
                Discoverer::Ptr dircoverer;
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = conns.find(c);
                    if (it == conns.end())
                    {
                        dircoverer = std::make_shared<Discoverer>(c);
                        conns.insert(std::make_pair(c, dircoverer));
                    }
                    else
                    {
                        dircoverer = it->second;
                    }

                    discoverers[method].insert(dircoverer);
                }

                dircoverer->AddMethod(method);
            }

            void DelDiscoverer(const BaseConnection::Ptr &c)
            {
                Discoverer::Ptr dircoverer;
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = conns.find(c);
                    if (it == conns.end())
                    {
                        return;
                    }

                    for (auto &e : it->second->_methods)
                    {
                        discoverers[e].erase(dircoverer);
                    }

                    conns.erase(c);
                }
            }
            //当有一个新的服务提供者上线，则进行上线通知
            void OnlineNotify(const std::string &method, const Address &host)
            {
                LOG(LogLevel::DEBUG)<<"服务提供者上线了";
                return notify(method, host, ServiceOptype::SERVICE_ONLINE);
            }
            // 当有一个服务提供者断开连接，则进行下线通知
            void OfflineNotify(const std::string &method, const Address &host)
            {
                LOG(LogLevel::DEBUG)<<"服务提供者下线了";
                return notify(method, host, ServiceOptype::SERVICE_OFFLINE);
            }

            //负载情况通知
            void Overloadreq(const std::string& method,const ServiceRequest::Ptr &msg)
            {
                std::unique_lock<std::mutex> _lock(_mutex);
                auto it = discoverers.find(method);
                if (it == discoverers.end())
                {
                    return;
                }

                for (auto &e : it->second)
                {
                    e->_conn->Send(msg);
                }
            }

        private:
            void notify(const std::string &method, const Address &host, ServiceOptype optype)
            {
                std::unique_lock<std::mutex> _lock(_mutex);
                auto it = discoverers.find(method);
                if (it == discoverers.end())
                {
                    return;
                }

                // 组织响应信息
                auto msg = MessageFactory::create<ServiceRequest>();
                msg->SetId(UUID::uuid());
                msg->SetMType(MType::REQ_SERVICE);
                msg->SetMethod(method);
                msg->SetOptype(optype);
                msg->SetHost(host);

                for (auto &e : it->second)
                {
                    e->_conn->Send(msg);
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Discoverer::Ptr>> discoverers;
            std::unordered_map<BaseConnection::Ptr, Discoverer::Ptr> conns;
        };


        class PDManager {
            public:
                using Ptr = std::shared_ptr<PDManager>;
                PDManager():
                    _providers(std::make_shared<ProviderManager>()),
                    _discoverers(std::make_shared<DiscovererManager>())
                {}
                void OnServiceRequest(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
                {
                    if(msg->Optype() == ServiceOptype::SERVICE_REGISTRY)
                    {
                        _providers->addProvider(conn,msg->Host(),msg->Method());
                        _discoverers->OnlineNotify(msg->Method(),msg->Host());
                        LOG(LogLevel::DEBUG)<<"添加注册者成功";
                        LOG(LogLevel::DEBUG)<<"ip:"<<msg->Host().first<<"  port: "<<msg->Host().second;
                        return registryResponse(conn,msg);
                    }
                    else if(msg->Optype() == ServiceOptype::SERVICE_DISCOVERY)
                    {
                        _discoverers->AddDiscoverer(conn,msg->Method());
                        LOG(LogLevel::DEBUG)<<"添加发现者成功";
                        return discoveryResponse(conn,msg);
                    }
                    else if(msg->Optype() == ServiceOptype::SERVICE_OVERLOAD)
                    {

                        _discoverers->Overloadreq(msg->Method(),msg);
                        LOG(LogLevel::DEBUG)<<"更新服务器负载";
                    }
                    else
                    {
                        LOG(LogLevel::ERROR)<<"服务类型出错";
                        return errorResponse(conn,msg);
                    }
                }

                void OnConnShutdown(const BaseConnection::Ptr &conn)
                {
                    //假设提供者下线
                    auto provider = _providers->getProvider(conn);
                    if(provider.get() != nullptr)   //是提供者
                    {
                        for(auto &e : provider->_methods)
                        {
                            _discoverers->OfflineNotify(e,provider->_host);
                        }
    
                        _providers->delProvider(conn);
                    }

                   _discoverers->DelDiscoverer(conn);
                }

            private:
        
                void errorResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
                {
                    auto rsp_msg = MessageFactory::create<ServiceResponse>();
                    rsp_msg->SetId(msg->Id());
                    rsp_msg->SetMType(MType::RSP_SERVICE);
                    rsp_msg->SetRcode(RCode::RCODE_INVALID_OPTYPE);
                    rsp_msg->SetOptype(ServiceOptype::SERVICE_UNKNOW);
                    conn->Send(rsp_msg);
                }
                void registryResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
                {
                    auto rsp_msg = MessageFactory::create<ServiceResponse>();
                    rsp_msg->SetId(msg->Id());
                    rsp_msg->SetMType(MType::RSP_SERVICE);
                    rsp_msg->SetRcode(RCode::RCODE_OK);
                    rsp_msg->SetOptype(ServiceOptype::SERVICE_REGISTRY);
                    conn->Send(rsp_msg);
                }
                void discoveryResponse(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &msg)
                {
                    auto rsp_msg = MessageFactory::create<ServiceResponse>();
                    rsp_msg->SetId(msg->Id());
                    rsp_msg->SetMType(MType::RSP_SERVICE);
                    rsp_msg->SetOptype(ServiceOptype::SERVICE_DISCOVERY);

                    std::vector<Address> res = _providers->methodHosts(msg->Method());
                    for(auto &e : res)
                    {
                        LOG(LogLevel::DEBUG)<<"响应发现者";
                        LOG(LogLevel::DEBUG)<<"ip:"<<e.first<<"  port: "<<e.second;
                    }
                    if(res.empty())
                    {
                        rsp_msg->SetRcode(RCode::RCODE_NOT_FOUND_SERVICE);
                        return conn->Send(rsp_msg);
                    }

                    rsp_msg->SetRcode(RCode::RCODE_OK);
                    rsp_msg->SetHost(res);
                    rsp_msg->SetMethod(msg->Method());
                    return conn->Send(rsp_msg);
                }
            private:
                ProviderManager::Ptr _providers;
                DiscovererManager::Ptr _discoverers;
        };

    }

}